package com.hitqz.robot.biz.schedule;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.google.common.cache.Cache;
import com.google.common.collect.Maps;
import com.hitqz.robot.api.common.dto.mapArea.CalculateAreaMapDto;
import com.hitqz.robot.api.common.entity.AreaAction;
import com.hitqz.robot.api.common.entity.MapAreaAction;
import com.hitqz.robot.api.common.entity.Robot;
import com.hitqz.robot.api.common.util.CacheNameUtil;
import com.hitqz.robot.biz.business.util.CalculateUtil;
import com.hitqz.robot.biz.factory.AreaActionFactory;
import com.hitqz.robot.biz.service.AreaActionService;
import com.hitqz.robot.biz.service.MapAreaActionService;
import com.hitqz.robot.biz.service.RobotService;
import com.hitqz.robot.driver.model.IRobotPosDto;
import com.hitqz.robot.driver.parent.RobotDriverService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.locationtech.jts.geom.Polygon;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * @author xupkun
 * @date 2024/8/19
 */
@Component
@RequiredArgsConstructor
@Slf4j
public class AreaActionSchedule {

    public static ThreadPoolExecutor threadPool = new ThreadPoolExecutor(10,20,
            1L, TimeUnit.SECONDS,
            new LinkedBlockingQueue<Runnable>(20),
            Executors.defaultThreadFactory(),
            new ThreadPoolExecutor.DiscardPolicy());

    public Map<Long, Double> robotAreaDistance= Maps.newHashMap();


    private final AreaActionFactory areaActionFactory;

    private final MapAreaActionService mapAreaActionService;

    private final AreaActionService areaActionService;

    private final RobotService robotService;

    private final RobotDriverService robotDriverService;

    private static final String MAP_AREA_CACHE_NAME = CacheNameUtil.CACHE_MAP_AREA;
    private static final int DECIMAL_PLACES = 3;


    @Scheduled(cron = "*/1 * * * * ?")
    public void calArea() {
        Robot robot = robotService.getDefaultRobot();
        if (robot == null) {
            return;
        }
        IRobotPosDto iRobotPosDto = robotDriverService.getWebPos();
        if (!isRobotReadyForCalculation(iRobotPosDto)) {
            return;
        }
        // 车体的矩形
        BigDecimal robotWidth = new BigDecimal(robot.getRobotWidth())
                .divide(BigDecimal.valueOf(1000), DECIMAL_PLACES, RoundingMode.HALF_UP);
        BigDecimal robotLength = new BigDecimal(robot.getRobotLength())
                .divide(BigDecimal.valueOf(1000), DECIMAL_PLACES, RoundingMode.HALF_UP);
        Polygon carPolygon = CalculateUtil.getRectangleVertices(iRobotPosDto.getX(),
                iRobotPosDto.getY(), robotWidth.doubleValue(), robotLength.doubleValue());
        Cache<Long, CalculateAreaMapDto> cache = SpringUtil.getBean(MAP_AREA_CACHE_NAME);
        for (Long areaId : cache.asMap().keySet()) {
            CalculateAreaMapDto mapDto = cache.getIfPresent(areaId);
            if (mapDto != null) {
                Polygon areaPolygon = mapDto.getPolygon();
                double distance = carPolygon.distance(areaPolygon);
                List<MapAreaAction> mapAreaActions = mapAreaActionService.list(Wrappers.<MapAreaAction>lambdaQuery()
                        .eq(MapAreaAction::getAreaId, areaId));
                // 0 一定要执行
                Double previousDistance = robotAreaDistance.put(areaId, distance);
                int actionType = 0;
                if (previousDistance != null) {
                    if (Math.abs(previousDistance - distance) < 0.01) {
                        previousDistance = distance;
                    }
                    if (previousDistance < distance) {
                        actionType = 1;
                    } else if (previousDistance > distance) {
                        actionType = 1;
                    } else if (carPolygon.intersects(areaPolygon) && carPolygon.overlaps(areaPolygon)) {
                        actionType = 2;
                    }
                }
                List<MapAreaAction> filteredActions = filterMapAreaActions(mapAreaActions, actionType);
                try {
                    executeAreaAction(distance, actionType, filteredActions);
                    // 一定要执行的
                    executeAreaAction(distance, 0, filterMapAreaActions(mapAreaActions, 0));
                } catch (Exception e) {
                    log.error("Error processing area actions: ", e);
                }
            }
        }
    }


    private boolean isRobotReadyForCalculation(IRobotPosDto dto) {
        //return dto != null && dto.getTaskStatus() != null && dto.getTaskStatus() == 2 && (dto.getSpeed() != 0.0);
        return dto != null;
    }



    /**
     * 执行任务
     * @param actions
     */
    private void executeAreaAction(double currentDistance, Integer runType, List<MapAreaAction> actions) {
        if (CollectionUtil.isEmpty(actions)) {
            log.warn("No actions for runType: " + runType);
            return;
        }
        // 根据 runType 调用不同的方法
        //  0-随时执行 1-进入区域前 2-在区域内 3-离开区域
        switch (runType) {
            case 0:
                executeActions(currentDistance, actions, "doActionAnyWhere");
                break;
            case 2:
                executeActions(currentDistance, actions, "doActionInArea");
                break;
            case 1:
                executeActions(currentDistance, actions, "doActionEnterArea");
                break;
            case 3:
                executeActions(currentDistance, actions, "doActionLeaveArea");
                break;
            default:
                log.error("Invalid runType: " + runType);
                break;
        }
    }

    // 提取的公共方法
    private void executeActions(double currentDistance, List<MapAreaAction> actions, String methodName) {
        threadPool.execute(() -> {
            actions.forEach(t -> {
                try {
                    AreaAction areaAction = areaActionService.getById(t.getActionId());
                    if (areaAction == null) {
                        log.error("Action not found for id: " + t.getActionId());
                        return;
                    }
                    areaActionFactory.getRoutingStrategy(areaAction.getAction())
                            .getClass()
                            .getMethod(methodName, double.class, String.class)
                            .invoke(areaActionFactory.getRoutingStrategy(areaAction.getAction()), currentDistance, t.getParams());
                } catch (Exception e) {
                    log.error("Error executing action with id: " + t.getActionId(), e);
                }
            });
        });
    }


    private List<MapAreaAction> filterMapAreaActions(List<MapAreaAction> actions, Integer runType) {
        return actions.stream()
                .filter(t -> t.getRunType() == null || Objects.equals(t.getRunType(), runType))
                .toList();
    }





}
